/*
 * ============================================================================
 *
 *       Filename:  aes.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  Tuesday, September 08, 2020 10:30:38 CST
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  jianxi sun (jianxi), ycsunjane@gmail.com
 *   Organization:  
 *
 * ============================================================================
 */
#include <stdio.h>
#include <stdint.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/aes.h>

int base64_encode(char *in_str, int in_len, char *out_str)
{
	BIO *b64, *bio;
	BUF_MEM *bptr = NULL;
	size_t size = 0;

	if (in_str == NULL || out_str == NULL)
		return -1;

	b64 = BIO_new(BIO_f_base64());
	bio = BIO_new(BIO_s_mem());
	bio = BIO_push(b64, bio);

	BIO_write(bio, in_str, in_len);
	BIO_flush(bio);

	BIO_get_mem_ptr(bio, &bptr);
	memcpy(out_str, bptr->data, bptr->length-1);
	out_str[bptr->length-1] = '\0';
	size = bptr->length-1;

	BIO_free_all(bio);
	return size;
}

void aes_cbc_pcsk5_encrypt(char* pcInput, int nLen, char* pcOut, char *key, char *iv)
{
	//char key[17] = "gilg5DzvM78zsdHn";
	//char iv[17] = "kGLZVcln0A2n0e0M";
	char encrypt_string[1024] = { 0 };
	AES_KEY aes;
	int n = 0;

	int nBei = nLen / AES_BLOCK_SIZE + 1;
	int nTotal = nBei * AES_BLOCK_SIZE;
	char *enc_s = (char*)malloc(nTotal);
	int nNumber = 0;
	//printf("nBei=%d, nTotal=%d,nLen=%d\n",nBei, nTotal, nLen);

	//KCS5Padding：填充的原则是,如果长度少于16个字节，需要补满16个字节，补(16-len)个(16-len)例如：
	//"31325980"这个节符串是8个字节，16-8=8,补满后如：31325980+8个十进制的8
	//如果字符串长度正好是16字节，则需要再补16个字节的十进制的16。
	if (nLen % 16 > 0) {
		nNumber = nTotal - nLen;
		//printf("number=%d\n", nNumber);
	} else {
		nNumber = 16;
	}

	memset(enc_s, nNumber, nTotal);
	memcpy(enc_s, pcInput, nLen);
	//printf("enc_s=%s\n", enc_s);

    //设置加密密钥，16字节
	if (AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0)
	{
		fprintf(stderr, "Unable to set encryption key in AES\n");
		exit(-1);
	}

	AES_cbc_encrypt((unsigned char *)enc_s, (unsigned char*)encrypt_string,
		nTotal, &aes, (unsigned char*)iv, AES_ENCRYPT);
	n = strlen(encrypt_string);
	//printf("encrypt_string n:%d, %ld\n", n, sizeof(encrypt_string));

	base64_encode(encrypt_string, nTotal, pcOut);
	n = strlen(pcOut);
	//printf("n:%d\n", n);

	free(enc_s);
}

int main(int argc, char** argv)
{
	//char* input_string = "test";
	//int nLen = strlen(input_string);
	char key[17] = "gilg5DzvM78zsdHn";

	char iv[17] = "kGLZVcln0A2n0e0M";

	char outstring[1024] = {0};

	//printf("AES_BLOCK_SIZE=%d\n", AES_BLOCK_SIZE);

	if(argc == 3) {
		int len = strlen(argv[1]);
		aes_cbc_pcsk5_encrypt(argv[1], len, outstring, argv[2], iv);
		//printf("argv[0] %s, argv[1] %s, argv[2] %s\n",
		//	argv[0], argv[1], argv[2]);
		printf("%s\n", outstring);
		return 0;
	} else if(argv[1]){
		aes_cbc_pcsk5_encrypt(argv[1], strlen(argv[1]), outstring, key, iv);
		printf("%s\n", outstring);
		return 0;
	} else {
		printf("usage : ./a.out Plaintext keyString\n");
		return 1;
	}


	// test code 
	//aes_cbc_pcsk5_encrypt(input_string, nLen, outstring, key, iv);
	//printf("%s\n", str2);

	return 0;
}

